import datetime
from typing import Optional, List, Dict, Any, Union

from peewee import Model, DateTimeField, BigAutoField, BooleanField, SQL, BigIntegerField, CharField, SmallIntegerField, Field
from peewee_async import Manager

from common.g.g import DB_PROXY

the_cast_keys = ("INT", "BIGINT", "SMALLINT", "BOOL", "FLOAT", "DOUBLE", "DECIMAL")


def peewee_normalize_dict(model: Union[Model],
                          obj: dict,
                          excludes: Optional[List[str]] = None) -> Dict[Field, Any]:
    """
    将dict字典转换成模型类的field=value格式，并且对于不存在的字段直接忽略
    :param model:
    :param obj:
    :param excludes:
    :return:
    """
    record = {}
    for k, v in obj.items():
        if v is None or (excludes and k in excludes):
            continue
        if getattr(Model, k, None):
            continue
        key = getattr(model, k, None)
        if key is None or (v == "" and key.field_type in the_cast_keys):
            continue
        record[key] = v
    return record


# =============Model===================
class AbstractModel(Model):

    class Meta:
        abstract = True
        database = DB_PROXY

    create_time = DateTimeField(default=datetime.datetime.now, help_text="创建时间")
    update_time = DateTimeField(default=datetime.datetime.now, constraints=[SQL("on update current_timestamp")],
                                help_text="更新时间")
    delete_time = DateTimeField(null=True, help_text="删除时间")
    removed = BooleanField(default=False, help_text="逻辑删除标志位")
    version = BigIntegerField(default=1, help_text="版本号")

    @classmethod
    def _parse_eq_condition(cls, condition: Dict[str, Any] = None):
        """
        等值条件判断
        :param condition:
        :return:
        """
        if condition is None or not condition:
            return [1==1]
        return [getattr(cls, k, None) == v for k, v in condition.items() if getattr(cls, k, None) is not None]

    @classmethod
    def _parse_fields(cls, fields):
        if not fields:
            return []
        res = [getattr(cls, k) for k in fields if getattr(cls, k, None)] if fields else None
        return res

    @classmethod
    def _all_fields(cls):
        return cls._meta.sorted_fields

    @classmethod
    async def add_new_instance(cls, mgw: Manager, **kwargs) -> int:
        d = peewee_normalize_dict(cls, kwargs)
        query = cls.insert(d)
        return await mgw.execute(query)

    @classmethod
    async def remove_instance(cls, mgw: Manager, *, condition) -> int:
        cond = cls._parse_eq_condition(condition=condition)
        query = cls.update(removed=True, delete_time=datetime.datetime.now()).where(*cond)
        return await mgw.execute(query)

    @classmethod
    async def get_instance(cls, mgw: Manager, *, condition: Dict[str, Any], fields: List[str] = None):
        """
        只支持等值判断
        """
        _fields = cls._parse_fields(fields) or cls._all_fields()
        cond = cls._parse_eq_condition(condition=condition)
        query = cls.select(*_fields).where(*cond)
        return await mgw.get(query.dicts())

    @classmethod
    async def search_instances(cls,
                               mgw: Manager,
                               *,
                               condition: Dict[str, Any] = None,
                               fields: List[str] = None):
        _fields = cls._parse_fields(fields) or cls._meta.sorted_fields
        cond = cls._parse_eq_condition(condition=condition)
        query = cls.select(*_fields).where(*cond).dicts()
        return await mgw.execute(query)

    @classmethod
    async def update_instance(cls, mgw: Manager, *, condition: Dict[str, Any], update_fields: Dict[str, Any]):
        d = peewee_normalize_dict(cls, update_fields)
        cond = cls._parse_eq_condition(condition=condition)
        query = cls.update(d).where(*cond)
        return await mgw.execute(query)

    @classmethod
    async def update_by_instance_with_version(cls,
                                              mgw: Manager,
                                              instance: Model,
                                              *,
                                              update_fields: Dict[str, Any] = None
                                              ) -> int:
        update_fields["version"] = instance.version + 1
        d = peewee_normalize_dict(cls, update_fields)
        query = instance.update(d).where(version=instance.version)
        return await mgw.execute(query)

    @classmethod
    async def exist(cls, mgw: Manager, field_to_value_dict: Dict[str, Any]):
        cond = cls._parse_eq_condition(condition=field_to_value_dict)
        query = cls.select(cls._pk).where(*cond)
        res = await mgw.execute(query)
        return bool(res)


class AbstractModelWithID(AbstractModel):

    class Meta:
        abstract = True

    id = BigAutoField(primary_key=True)


# ==============Field========================
class IntEnumField(SmallIntegerField):

    def __iter__(self):
        pass

    def db_value(self, value):
        if hasattr(value, "value"):
            return value.value
        else:
            return value

    def python_value(self, value):
        if value:
            return self.choices(value)
        return value


class StrEnumField(CharField):

    def __iter__(self):
        pass

    def db_value(self, value):
        if hasattr(value, "value"):
            return value.value
        return value

    def python_value(self, value):
        return self.choices(value)



